/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package linkeddata.arida.ufc.br.manager.impl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import linkeddata.arida.ufc.br.manager.HierarchyProvider;
import linkeddata.arida.ufc.br.manager.ObjectPropertyProvider;
import linkeddata.arida.ufc.br.util.OWLUtils;
import linkeddata.arida.ufc.br.util.TripleStore;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLObjectUnionOf;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.reasoner.NodeSet;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.util.DefaultPrefixManager;

/**
 *
 * @author jeovane
 */
public class BuildsHierarchyDataProperty extends OWLUtils implements HierarchyProvider<Object>, ObjectPropertyProvider<Object> {

    private OWLOntology ontology;
    private OWLReasoner reasoner;
    private DefaultPrefixManager pm;

    public BuildsHierarchyDataProperty(OWLOntology ontology, OWLReasoner reasoner, DefaultPrefixManager pm) {
        this.ontology = ontology;
        this.reasoner = reasoner;
        this.pm = pm;
    }
    
    @Override
    public Map<Object, ArrayList<OWLEntity>> buildsHierarchy() {
        Map<Object, Object> mapp = new TreeMap<Object, Object>();
        Map<Object, ArrayList<OWLEntity>> map = new TreeMap<Object, ArrayList<OWLEntity>>();
        ArrayList<OWLEntity> arrayExistente;

        for (OWLDataProperty odp : ontology.getDataPropertiesInSignature()) {
            // System.out.println(odp);
            for (OWLClassExpression oce : odp.getDomains(ontology)) {
                // System.out.println(odp + " " + oce);

                // Caso um dataproperty tenha mais de um Domain, então ele é uma instancia de um OWLObjectUnionOf
                // então, para não adicionar apenas a referência do dataproperty ao último Domain
                // adiciona-os a um ArrayList guardando a referência da Key
                if (oce instanceof OWLObjectUnionOf) {
                    Set<OWLClassExpression> asDisjunctSet = oce.asDisjunctSet();
                    for (Iterator<OWLClassExpression> it = asDisjunctSet.iterator(); it.hasNext();) {
                        OWLClassExpression oWLClassExpression = it.next();
                        // System.out.println("oWLClassExpression: " + oWLClassExpression);
                        // System.out.println("odp: " + odp);
                        arrayExistente = new ArrayList<OWLEntity>();
                        arrayExistente.add(odp);
                        map.put(oWLClassExpression, arrayExistente);
                    }
                } else {
                    // System.out.println(odp);
                    mapp.put(odp, oce);
                }
            }
        }

        for (OWLClass oWLClass : ontology.getClassesInSignature()) {
            // System.out.println(oWLClass.toString());
            for (Map.Entry<Object, Object> entry : mapp.entrySet()) {
                if (oWLClass.equals(entry.getValue())) {
                    // System.out.println("    " + entry.getKey().toString());
                    if (map.containsKey(oWLClass)) {
                        // Caso a Key já esteja adicionada no Map<K,V> para não sobreescrever o resultado já existente
                        // adiciona-se o value ao array, no entanto, referenciando para a Key na qual ele pertence
                        arrayExistente = map.get(oWLClass);
                        arrayExistente.add((OWLEntity) entry.getKey());
                        map.put(oWLClass, arrayExistente);
                    } else {
                        // Adiciona os valores no Map<K,V> no qual a Key ainda não foi adicionada
                        // System.out.println("entry.getKey().toString(): " + entry.getKey().toString());
                        // System.out.println("entry.getValue(): " + entry.getValue());
                        ArrayList<OWLEntity> array = new ArrayList<OWLEntity>();
                        array.add((OWLEntity) entry.getKey());
                        map.put(entry.getValue(), array);
                    }
                }
            }
        }
        return map;
    }

    /*
     * Imprime as "ligações" definidas entre as classes da ontologia
     * Domain > Object Property > Range
     */
    @Override
    @SuppressWarnings("empty-statement")
    public ArrayList buildsObjectProperty() {
        ArrayList<TripleStore> arrayList = new ArrayList<TripleStore>();
        TripleStore tripleStore;
        NodeSet<OWLClass> clsDomain;
        Set<OWLClass> getDomain;
        NodeSet<OWLClass> clsRange;
        Set<OWLClass> getRange;

        for (OWLObjectPropertyExpression oope : ontology.getObjectPropertiesInSignature()) {
            tripleStore = new TripleStore();

            clsDomain = reasoner.getObjectPropertyDomains(oope, true);
            clsRange = reasoner.getObjectPropertyRanges(oope, true);
            getDomain = clsDomain.getFlattened();
            getRange = clsRange.getFlattened();

            if (!getDomain.isEmpty()) {
                for (OWLClass domainCls : getDomain) {
                    tripleStore.setDomain(pm.getShortForm(domainCls).replace(":", "").toString());
                }
            }

            tripleStore.setObjectProperty(pm.getShortForm((OWLEntity) oope).replace(":", "").toString());

            if (!getRange.isEmpty()) {
                for (OWLClass rangeCls : getRange) {
                    tripleStore.setRange(pm.getShortForm(rangeCls).replace(":", "").toString());
                }
            }
            arrayList.add(tripleStore);
        }
        return arrayList;
    }
//    @Override
//    @SuppressWarnings("empty-statement")
//    public ArrayList buildsObjectProperty(OWLOntology ontology) {
//        ArrayList<TripleStore> arrayList = new ArrayList<>();
//        TripleStore tripleStore;
//        for (OWLObjectProperty objectProperty : ontology.getObjectPropertiesInSignature()) {
//            for (Iterator<OWLClassExpression> itDomains = objectProperty.getDomains(ontology).iterator(); itDomains.hasNext();) {
//                OWLEntity oceDomains = (OWLEntity) itDomains.next();
//                for (Iterator<OWLClassExpression> itRanges = objectProperty.getRanges(ontology).iterator(); itRanges.hasNext();) {
//                    OWLEntity oceRanges = (OWLEntity) itRanges.next();;
//                    tripleStore = new TripleStore();
//
//                    tripleStore.setDomain(getPm().getShortForm(oceDomains).replace(":", ""));
//                    tripleStore.setObjectProperty(getPm().getShortForm(objectProperty).replace(":", ""));
//                    tripleStore.setRange(getPm().getShortForm(oceRanges).replace(":", ""));
//                    arrayList.add(tripleStore);
//                }
//            }
//        }
//        return arrayList;
//    }
}
